package com.zzh.framework;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName zzhApplicationContext
 * @date 2021/4/5 19:58
 * @Version 1.0
 * @Description //模仿spring启动
 * @USER root
 */
public class zzhApplicationContext {

    private Class configClass;
    //存放扫描到的bean
    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();
    //单例池
    private Map<String,Object> singletonObjects = new HashMap<>();

    public zzhApplicationContext(Class configClass) {
        this.configClass = configClass;
        //扫描--扫描某个类创建一个beanDefinition
        scan(configClass);

        //创建一个非懒加载的bean
        createNoLazyBean();
    }

    private void createNoLazyBean() {
        for (String beanName:beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton") && !beanDefinition.isLazy()) {
                Object bean = createBean(beanDefinition,beanName);
                singletonObjects.put(beanName,bean);
            }
        }
    }

    private Object createBean(BeanDefinition beanDefinition,String beanName) {
        Class beanClass = beanDefinition.getBeanClass();
        try {
            //实例化一个对象
            Object instance = beanClass.getDeclaredConstructor().newInstance();
            //填充一个对象的属性
            for (Field filed:beanClass.getDeclaredFields()) {
                //如果加上了autowired注解了 再去自动注入
                if (filed.isAnnotationPresent(Autowired.class)) {
                    //先byType再byName
                    Object bean = getBean(filed.getName());
                    filed.setAccessible(true);
                    filed.set(instance,bean);
                }
            }
            //...
            //实现自动填充beanName
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }
            //最后才执行的
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }
            //aop

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
    private void scan(Class configClass) {
        //扫描
        //判断哪个类使用是componentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            //拿到注解的信息
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            //扫描路径 得到的是包名
            String path = componentScanAnnotation.value();
            path = path.replace(".","/");
            ClassLoader classLoader = zzhApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            File file = new File(resource.getFile());
            // 拿到文件 F:\InJIDEAworkspeace\study\springDemo\target\classes\com\zzh\service\UserService.class
            for (File listFile:file.listFiles()) {
               //路径
                String absolutePath = listFile.getAbsolutePath();
                if (absolutePath.endsWith(".class")) {
                    absolutePath =absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                    absolutePath = absolutePath.replace("\\",".");
                    try {
                        Class clazz = classLoader.loadClass(absolutePath);

                        if (clazz.isAnnotationPresent(Component.class)) {
                            //这是bean

                            BeanDefinition beanDefinition = new BeanDefinition();

                            Component componentAnnotation = (Component) clazz.getAnnotation(Component.class);
                            //bean名字
                            String beanName = componentAnnotation.value();

                            if (clazz.isAnnotationPresent(Lazy.class)) {
                                // 是否为懒加载
                                beanDefinition.setLazy(true);
                            }else {
                                beanDefinition.setLazy(false);
                            }
                            //是否为单例
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = (Scope) clazz.getAnnotation(Scope.class);
                                String value = scopeAnnotation.value();
                                beanDefinition.setScope(value);

                            } else {
                                beanDefinition.setScope("singleton");
                            }
                            beanDefinition.setBeanClass(clazz);
                            beanDefinitionMap.put(beanName,beanDefinition);

                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
    }

    public  Object getBean(String beanName) {
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new NullPointerException();
        } else {

            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")) {
                //单例池
                Object o = singletonObjects.get(beanName);
                //如果没有需要去创建
                if (null == o) {
                    o = createBean(beanDefinition,beanName);
                    singletonObjects.put(beanName,o);
                }
                return o;
            } else if (beanDefinition.getScope().equals("prototype")){
                //创建一个bean
                Object bean = createBean(beanDefinition,beanName);
                return bean;
            }
        }
        return null;
    }


}
